Imports System.Drawing

''' <summary>
''' The YAxis object describes the properties of either the Y1 or Y2 Axis including label values and display font.
''' </summary>
''' <remarks></remarks>
Public Class YAxis
    ''' <summary>
    ''' Indicates the position of this Y Axis on the chart (left side or right side)
    ''' </summary>
    ''' <remarks></remarks>
    Public Enum Alignment As Integer
        Left = 1
        Right = 2
    End Enum

    '---Overall dimensions of chart
    Dim moChartDimensions As ChartDimensions

    '---Appearance
    Protected msAxisName As String
    Protected moAxisFont As Font = New Font(System.Drawing.FontFamily.GenericSansSerif, 10)

    Protected miLabelMargin As Integer = 5
    Protected mbShowLines As Boolean = True
    Protected miAxisAlignment As Alignment
    Protected moFontColor As Color = Color.Black
    Protected moFont As Font = New Font(System.Drawing.FontFamily.GenericSansSerif, 10)

    '---Dimensions of Y-Axis label area
    Protected miYAxisLabelTop As Integer
    Protected miYAxisLabelLeft As Integer

    Protected miYAxisLabelWidth As Integer
    Protected miYAxisLabelHeight As Integer

    Protected miYAxisLabelBottom As Integer
    Protected miYAxisLabelRight As Integer

    Protected moYAxisValues As Single()
    Protected moYAxisLabels As String()
    Protected msYAxisLabelFormat As String = ""

    Protected miYAxisLabelCount As Integer
    Protected miYAxisLabelBoxHeight As Integer

    ''' <summary>
    '''  iAlignment is a jChart.YAxis.Alignment value that indicates whether this Y Axis should be located on the left side of the 
    ''' chart (Y1) or on the right side (Y2). oChartDimensions is a ChartDimensions object describing the interior of the chart.
    ''' iValueCount is the number of labels that should be drawn on the Y Axis. snValueMin is the Y Axis value at the bottom of 
    ''' the Y Axis and snValueMax is the value at the top of the Y Axis. Values are evenly distributed between snValueMin and snValueMax. 
    ''' </summary>
    ''' <param name="iAlignment"></param>
    ''' <param name="oChartDimensions"></param>
    ''' <param name="iValueCount"></param>
    ''' <param name="snValueMin"></param>
    ''' <param name="snValueMax"></param>
    ''' <remarks></remarks>
    Public Sub New( _
                    ByVal iAlignment As Alignment, _
                    ByVal oChartDimensions As ChartDimensions, _
                    ByVal iValueCount As Integer, _
                    ByVal snValueMin As Single, _
                    ByVal snValueMax As Single)

        Dim snInc As Single = (snValueMax - snValueMin) / iValueCount

        Dim oValues(iValueCount) As Single
        ReDim moYAxisLabels(iValueCount)

        oValues(0) = snValueMin
        moYAxisLabels(0) = snValueMin

        For iValue As Integer = 1 To iValueCount
            oValues(iValue) = snValueMin + snInc * iValue

            moYAxisLabels(iValue) = CStr(oValues(iValue))
        Next

        Init(iAlignment, oChartDimensions, oValues)
    End Sub

    ''' <summary>
    ''' iAlignment is a jChart.YAxis.Alignment value that indicates whether this Y Axis should be located 
    ''' on the left side of the chart (Y1) or on the right side (Y2). oChartDimensions is a ChartDimensions 
    ''' object describing the interior of the chart and oYAxisValues() is an array of Single containing a 
    ''' value for each point on the Y Axis in bottom to top order.
    ''' </summary>
    ''' <param name="iAlignment"></param>
    ''' <param name="oChartDimensions"></param>
    ''' <param name="oYAxisValues"></param>
    ''' <remarks></remarks>
    Public Sub New( _
                    ByVal iAlignment As Alignment, _
                    ByVal oChartDimensions As ChartDimensions, _
                    ByVal oYAxisValues() As Single)

        Init(iAlignment, oChartDimensions, oYAxisValues)
    End Sub

    Private Sub Init( _
                    ByVal iAlignment As Alignment, _
                    ByVal oChartDimensions As ChartDimensions, _
                    ByVal oYAxisValues() As Single)

        miAxisAlignment = iAlignment

        moChartDimensions = oChartDimensions
        Me.YAxisValues = oYAxisValues
    End Sub

    ''' <summary>
    ''' The title of the Y Axis
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property AxisName() As String
        Get
            Return msAxisName
        End Get
        Set(ByVal value As String)
            msAxisName = value
        End Set
    End Property

    ''' <summary>
    ''' The System.Drawing.Font object to use when displaying the Y Axis title
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property AxisFont() As Font
        Get
            Return moAxisFont
        End Get
        Set(ByVal value As Font)
            moAxisFont = value
        End Set
    End Property

    ''' <summary>
    ''' An array of Single containing values for the Y Axis, in bottom to top order.
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property YAxisValues() As Single()
        Get
            Return moYAxisValues
        End Get
        Set(ByVal value As Single())
            moYAxisValues = value

            miYAxisLabelCount = moYAxisValues.GetUpperBound(0)
            miYAxisLabelBoxHeight = moChartDimensions.InnerHeight / (miYAxisLabelCount + 1)

            ReDim moYAxisLabels(value.GetUpperBound(0))
            For i As Integer = 0 To value.GetUpperBound(0)
                moYAxisLabels(i) = value(i)
            Next
        End Set
    End Property

    ''' <summary>
    ''' An array of String containing labels to be displayed in place of values 
    ''' for the Y Axis, in bottom to top order. This array should have the same 
    ''' count of values as YAxisValues().
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property YAxisLabels() As String()
        Get
            Return moYAxisLabels
        End Get
        Set(ByVal value As String())
            moYAxisLabels = value
        End Set
    End Property

    ''' <summary>
    ''' 
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks>
    ''' http://blogs.msdn.com/kathykam/archive/2006/03/29/564426.aspx
    ''' </remarks>
    Public Property YAxisLabelFormat() As String
        Get
            Return msYAxisLabelFormat
        End Get
        Set(ByVal value As String)
            msYAxisLabelFormat = value
        End Set
    End Property

    ''' <summary>
    ''' The font to use when drawing Y Axis labels.
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property Font() As Font
        Get
            Return moFont
        End Get
        Set(ByVal value As Font)
            moFont = value
        End Set
    End Property

    ''' <summary>
    ''' The color to use when drawing Y Axis labels.
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property FontColor() As Color
        Get
            Return moFontColor
        End Get
        Set(ByVal value As Color)
            moFontColor = value
        End Set
    End Property

    ''' <summary>
    ''' True if horizontal lines should be drawn for each y axis value.
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property ShowLines() As Boolean
        Get
            Return mbShowLines
        End Get
        Set(ByVal value As Boolean)
            mbShowLines = value
        End Set
    End Property

    Friend Property YAxisLabelCount() As Integer
        Get
            Return miYAxisLabelCount
        End Get
        Set(ByVal value As Integer)
            miYAxisLabelCount = value
        End Set
    End Property

    Friend Property YAxisLabelBoxHeight() As Integer
        Get
            Return miYAxisLabelBoxHeight
        End Get
        Set(ByVal value As Integer)
            miYAxisLabelBoxHeight = value
        End Set
    End Property

    Friend Sub RecalculateDimensions(ByVal g As Graphics)
        Dim oTitleSize As SizeF = g.MeasureString(msAxisName, moAxisFont)

        Dim iColumnWidth As Integer = 0

        For iCurrentLabel As Integer = 0 To miYAxisLabelCount
            g.TextRenderingHint = Text.TextRenderingHint.AntiAlias
            Dim oLabelSize As SizeF = g.MeasureString(moYAxisLabels(iCurrentLabel), moFont)
            g.TextRenderingHint = Text.TextRenderingHint.SystemDefault

            If iColumnWidth < oLabelSize.Width Then
                iColumnWidth = oLabelSize.Width
            End If
        Next

        If miAxisAlignment = Alignment.Left Then
            moChartDimensions.YAxis1LabelColumnWidth = iColumnWidth + miLabelMargin + oTitleSize.Height
        Else
            moChartDimensions.YAxis2LabelColumnWidth = iColumnWidth + miLabelMargin + oTitleSize.Height
        End If

        CalculateAxisDimensions()
    End Sub

    Private Sub CalculateAxisDimensions()
        '---Y Axis area dimensions
        miYAxisLabelTop = moChartDimensions.InnerTop
        miYAxisLabelHeight = moChartDimensions.InnerHeight
        miYAxisLabelBottom = miYAxisLabelTop + miYAxisLabelHeight

        If miAxisAlignment = Alignment.Left Then
            miYAxisLabelWidth = moChartDimensions.YAxis1LabelColumnWidth
            miYAxisLabelLeft = moChartDimensions.FrameWidthLeft
            miYAxisLabelRight = miYAxisLabelLeft + miYAxisLabelWidth
        Else
            miYAxisLabelWidth = moChartDimensions.YAxis2LabelColumnWidth
            miYAxisLabelLeft = moChartDimensions.InnerRight
            miYAxisLabelRight = miYAxisLabelLeft + miYAxisLabelWidth
        End If
    End Sub

    Friend Sub DrawYAxisLabelArea(ByRef g As Graphics)
        CalculateAxisDimensions()

        Dim oBrush As New SolidBrush(moChartDimensions.YAxis1BackgroundColor)
        Dim oRect As Rectangle

        If miAxisAlignment = Alignment.Left Then
            oRect = New Rectangle(miYAxisLabelLeft - 1, miYAxisLabelTop, miYAxisLabelWidth - 1, miYAxisLabelHeight)
        Else
            oRect = New Rectangle(miYAxisLabelLeft + 1, miYAxisLabelTop, miYAxisLabelWidth - 1, miYAxisLabelHeight)
        End If

        g.FillRectangle(oBrush, oRect)

        DrawYAxisValues(g)
    End Sub

    Friend Function CalculateYPos(ByVal snYValue As Single) As Integer
        Dim iMinYValue As Single = moYAxisValues(0)
        Dim iMaxYValue As Single = moYAxisValues(moYAxisValues.GetUpperBound(0))

        Dim snYRange As Single = iMaxYValue - iMinYValue
        Dim snPercent As Single = 0

        If snYRange <> 0 Then
            snPercent = 1 - (snYValue - iMinYValue) / snYRange
        End If

        Dim iYPos As Integer = moChartDimensions.OuterY + moChartDimensions.FrameWidthTop + (moChartDimensions.InnerHeight * snPercent) '- (moChartDimensions.XAxisLabelRowHeight - moChartDimensions.FrameWidth)

        Return iYPos
    End Function

    Private Sub DrawYAxisValues(ByRef g As Graphics)
        If mbShowLines = True Then
            DrawYAxisLines(g)
        End If

        DrawYAxisLabels(g)
    End Sub

    Private Sub DrawYAxisLines( _
                    ByRef g As Graphics)

        For iCurrentLabel As Integer = 0 To miYAxisLabelCount
            Dim iYPos As Integer = CalculateYPos(moYAxisValues(iCurrentLabel))
            g.DrawLine(Pens.Blue, moChartDimensions.InnerLeft, iYPos, moChartDimensions.InnerRight, iYPos)
        Next
    End Sub

    Private Sub DrawYAxisLabels( _
                    ByRef g As Graphics)

        Dim oBrush As New SolidBrush(moFontColor)

        Dim sLabel As String

        For iCurrentLabel As Integer = 0 To miYAxisLabelCount
            If Not moYAxisLabels Is Nothing Then
                If msYAxisLabelFormat > "" Then
                    If IsNumeric(moYAxisLabels(iCurrentLabel)) Then
                        sLabel = String.Format(msYAxisLabelFormat, Val(moYAxisLabels(iCurrentLabel)))
                    Else
                        sLabel = String.Format(msYAxisLabelFormat, moYAxisLabels(iCurrentLabel))
                    End If
                Else
                    sLabel = moYAxisLabels(iCurrentLabel)
                End If
            Else
                If msYAxisLabelFormat > "" Then
                    If IsNumeric(moYAxisValues(iCurrentLabel)) Then
                        sLabel = String.Format(msYAxisLabelFormat, Val(moYAxisValues(iCurrentLabel)))
                    Else
                        sLabel = String.Format(msYAxisLabelFormat, moYAxisValues(iCurrentLabel))
                    End If
                Else
                    sLabel = moYAxisValues(iCurrentLabel)
                End If
            End If

            g.TextRenderingHint = Text.TextRenderingHint.AntiAlias
            Dim iTextHeight As Integer = g.MeasureString(sLabel, moFont).Height
            Dim iTextWidth As Integer = g.MeasureString(sLabel, moFont).Width
            g.TextRenderingHint = Text.TextRenderingHint.SystemDefault

            Dim iYPosMin As Integer = CalculateYPos(moYAxisValues(iCurrentLabel)) - iTextHeight / 2

            If miAxisAlignment = Alignment.Left Then
                g.DrawString(sLabel, moFont, oBrush, miYAxisLabelLeft + miYAxisLabelWidth - iTextWidth, iYPosMin)
            Else
                g.DrawString(sLabel, moFont, oBrush, miYAxisLabelLeft + miLabelMargin, iYPosMin)
            End If
        Next

        If msAxisName > "" Then
            g.TextRenderingHint = Text.TextRenderingHint.AntiAlias
            Dim oSize As SizeF = g.MeasureString(msAxisName, moAxisFont)
            g.TextRenderingHint = Text.TextRenderingHint.SystemDefault

            Dim oSF As New System.Drawing.StringFormat(System.Drawing.StringFormatFlags.DirectionVertical)
            If miAxisAlignment = Alignment.Left Then
                g.DrawString( _
                        msAxisName, _
                        moAxisFont, _
                        New SolidBrush(moFontColor), _
                        miYAxisLabelLeft, _
                        miYAxisLabelTop + miYAxisLabelHeight / 2 - oSize.Width / 2, _
                        oSF)

            Else
                g.DrawString( _
                        msAxisName, _
                        moAxisFont, _
                        New SolidBrush(moFontColor), _
                        miYAxisLabelLeft + miYAxisLabelWidth - oSize.Height, _
                        miYAxisLabelTop + miYAxisLabelHeight / 2 - oSize.Width / 2, _
                        oSF)

            End If
        End If
    End Sub

End Class
